/**
  * Autuor : zhaoshun
  * Time   : 2017/10/18 22:16
  * Email  : tmby1314@163.com
  */

/**
 * 参考：《操作系统习题与应用解析》 陆松年 2012年6月第一版
 * 编译器：gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
 */


#include <stdio.h>
#include <stdlib.h>

#define N 5
/**
 * 表的定义
 */
struct map{
    unsigned int m_size;
    char *m_addr;
};

struct map coremap[N];


/**
 * 首次适应分配函数
 * @param size
 * @return
 */
char *fmalloc(unsigned int size){
    register char *a;
    register struct map *bp;
    for(bp=coremap;bp->m_size;bp++){
        if(bp->m_size>=size){
            a=bp->m_addr;
            bp->m_addr+=size;
            if((bp->m_size-=size)==0){
                do{
                    bp++;
                    (bp-1)->m_addr=bp->m_addr;
                }while((bp-1)->m_size=bp->m_size);
            }
            return (a);
        }

    }
    return (0);
}
/**
 * 首次适应的释放函数
 * @param size
 * @param aa
 */
void ffree(unsigned int size,char *aa){
    struct map *bp;
    char *a,*t;
    unsigned int tt;
    a=aa;
    for(bp=coremap;bp->m_addr<=a&&bp->m_size!=0;bp++);
    if(bp>coremap&&(bp-1)->m_addr+(bp-1)->m_size==a){
        (bp-1)->m_size+=size;
        if(a+size==bp->m_addr){
            (bp-1)->m_size+=bp->m_size;
            while(bp->m_size){
                bp++;
                (bp-1)->m_addr=bp->m_addr;
                (bp-1)->m_size=bp->m_size;
            }
        }
    }else{
        if(a+size==bp->m_addr&&bp->m_size){
            bp->m_addr-=size;
            bp->m_size+=size;
        }else{
            if(size){
                do{
                    t=bp->m_addr;
                    bp->m_addr=a;
                    a=t;
                    tt=bp->m_size;
                    bp->m_size=size;
                    bp++;
                }while(size=tt);
            }
        }
    }

}

/**
 * coremap 表的初始化程序
 * @param base_addr
 * @param size
 */
void initCoremap(char *base_addr,unsigned int size){
    int i;
    coremap[0].m_addr=base_addr;
    coremap[0].m_size=size;
    for(i=1;i<N;i++){
        coremap[i].m_size=0;
        coremap[i].m_addr=0;
    }
}

/**
 * 输出表的内容
 * @param base_addr
 */
void printCoremap(char *base_addr){
    int i,zero=0;
    printf("		Size\tR_addr\t\tAddr\n");
    for(i=0;i<N;i++){
        if(coremap[i].m_size!=0){
            printf("coremap[%d]:%u\t\t%ld\t\t%p\n",i,coremap[i].m_size,coremap[i].m_addr-base_addr,coremap[i].m_addr);
        }else{
            printf("coremap[%d]:%u\t\t%u\t\t%u\n",i,coremap[i].m_size,zero,zero);
        }
    }
}
/**
 * 显示帮助信息
 */
void showHelp(){
    printf("m size :分配内存\n");
    printf("f size addr:释放内存\n");
    printf("p :打印coremap表\n");
    printf("h :显示帮助信息\n");
    printf("q :退出执行\n");
}

int main_memory_manager(int argc, char **argv){
    unsigned int size,total_size;
    char *base_addr,*addr,*start_addr,cmdchar;
    unsigned int r_addr;
    printf("请输入要分配的内存的大小：");
    scanf("%u",&total_size);
    base_addr=malloc(total_size);
    initCoremap(base_addr,total_size);
    do{
        printf("please input command,h for help.\n");
        do{
            cmdchar=getchar();
        }while(cmdchar==' '||cmdchar=='\t'||cmdchar=='\n');
        switch(cmdchar){
            case 'm':
                scanf("%u",&size);
                if(size<0||size>=total_size){
                    printf("参数size错误\n");
                    break;
                }
                start_addr=fmalloc(size);
                if(start_addr==0){
                    printf("没有足够的空闲分区\n");
                }
                break;
            case 'f':
                scanf("%u%u",&size,&r_addr);
                if(r_addr<0||r_addr>=total_size){
                    printf("释放空闲区的起始地址超出范围\n");
                    break;
                }
                addr=base_addr+r_addr;
                ffree(size,addr);
                break;
            case 'p':
                printCoremap(base_addr);
                break;
            case 'h':
                showHelp();
                break;
            case 'q':
                break;
            default:
                printf("非法命令，显示帮助信息，请输入h\n");
                continue;
        }
    }while(cmdchar!='q');
    free(base_addr);
    return 0;
}